home *** CD-ROM | disk | FTP | other *** search
- /*
- File: GetShapeLocalBounds.cp
-
- Contains: This file implements a custom version of GXGetShapeLocalBounds
- that fixes a problem with handling the bounds of an embedded
- QuickDraw picture.
-
- Written by: Daniel Lipton
-
- Copyright: © 1997 by Apple Computer, Inc., all rights reserved.
-
- Writers:
-
- (DIL) Daniel Lipton
- (IK) Ingrid Kelly
-
- Change History (most recent first):
-
- <1> 6/1/97 IK First created.
- */
-
- #include <GXGraphics.h>
-
- #include "GetShapeLocalBounds.h"
-
- /* ---------------------------------------------------------------------------
- The transform stack class. This is used for building a stack of
- concatenated transforms for doing the bounding box calculation in pictures.
- --------------------------------------------------------------------------- */
-
- class GXBoundingBoxTransformStack {
- public:
- GXBoundingBoxTransformStack();
- ~GXBoundingBoxTransformStack();
- void Push(gxTransform aTransform);
- void Pop();
- void AccumulateBounds(gxShape aShape);
- void GetBoundsOfMappedShape(gxShape aShape, gxRectangle *mappedBounds);
- gxRectangle* GetAccumulatedBounds(gxRectangle *bounds)
- { *bounds = accumulatedBounds; return bounds; };
- protected:
- gxRectangle rootClipBounds; /* The bounding box of the root clip. */
- gxRectangle accumulatedBounds; /* The bounds we've accumulated so far. */
- gxTransform* transformList; /* The list of transforms on the stack. */
- long transformCount; /* How many transforms on the stack. */
- long listSize; /* How big (in gxTransforms) is the list allocated. */
-
- gxTransform GetTop() {
- if ( transformCount > 0 ) return (transformList[transformCount-1]);
- else return nil;
- }
- };
-
- /* ---------------------------------------------------------------------------
- GXTestTransformIdentity
- --------------------------------------------------------------------------- */
-
- static Boolean GXTestTransformIdentity(gxTransform aTransform, gxShape *returnClip)
- {
- gxMapping theMapping;
- gxShape clip = GXGetTransformClip(aTransform);
- gxShapeType clipType = GXGetShapeType(clip);
- register Fixed *mapElement;
-
- if ( returnClip )
- *returnClip = clip;
- else
- GXDisposeShape(clip);
-
- if ( clipType != gxFullType )
- return false;
-
- /* If the clip was full, then proceed to test the mapping. */
-
- GXGetTransformMapping(aTransform, &theMapping);
- mapElement = &(theMapping.map[0][0]);
-
- /** Note, this expression depends on evaluation in left-right order. **/
-
- return ( (*mapElement++ == ff(1)) &&
- (*mapElement++ == ff(0)) &&
- (*mapElement++ == ff(0)) &&
-
- (*mapElement++ == ff(0)) &&
- (*mapElement++ == ff(1)) &&
- (*mapElement++ == ff(0)) &&
-
- (*mapElement++ == ff(0)) &&
- (*mapElement++ == ff(0)) &&
- (*mapElement == fract1)
- );
- }
-
- /* ---------------------------------------------------------------------------
- GXBoundingBoxTransformStack
- --------------------------------------------------------------------------- */
-
- GXBoundingBoxTransformStack::GXBoundingBoxTransformStack()
- {
- accumulatedBounds.left = gxPositiveInfinity;
- accumulatedBounds.top = gxPositiveInfinity;
- accumulatedBounds.right = gxNegativeInfinity;
- accumulatedBounds.bottom = gxNegativeInfinity;
- transformList = nil;
- transformCount = 0;
- listSize = 0;
- }
-
- /* ---------------------------------------------------------------------------
- ~GXBoundingBoxTransformStack
- --------------------------------------------------------------------------- */
-
- GXBoundingBoxTransformStack::~GXBoundingBoxTransformStack()
- {
- if ( transformList != nil )
- {
- for ( long idx = 0; idx < transformCount; ++idx )
- GXDisposeTransform(transformList[idx]);
-
- if ( transformList != nil)
- delete transformList;
- }
- }
-
- /* ---------------------------------------------------------------------------
- GetBoundsOfMappedShape
- --------------------------------------------------------------------------- */
-
- void GXBoundingBoxTransformStack::GetBoundsOfMappedShape(gxShape aShape, gxRectangle *mappedBounds)
- {
- gxShapeType theType = GXGetShapeType(aShape);
- gxShapeFill theFill = GXGetShapeFill(aShape);
- gxShape tempShape;
- gxMapping nestMap;
-
- if ( theFill == gxNoFill )
- {
- mappedBounds->left = gxPositiveInfinity;
- mappedBounds->top = gxPositiveInfinity;
- mappedBounds->right = gxNegativeInfinity;
- mappedBounds->bottom = gxNegativeInfinity;
- return;
- }
-
- /* Make a copy of the shape so we can take it into "root" space. */
-
- if ( theType != gxBitmapType )
- {
- tempShape = GXCopyToShape(nil, aShape);
-
- /* If it is framed, we need to apply the style to get accurate bounds. */
-
- if ( (theFill == gxFrameFill) || (theFill == gxClosedFrameFill) )
- if ( GXGetShapePen(tempShape) != 0 )
- GXPrimitiveShape(tempShape);
- }
- else
- {
- /* For bitmaps, we can just use a rectangle for our calculations */
-
- gxPoint location;
- gxBitmap theBits;
- gxRectangle bitRect;
-
- GXGetBitmap(aShape, &theBits, &location);
- bitRect.left = location.x;
- bitRect.top = location.y;
- bitRect.right = bitRect.left + ff(theBits.width);
- bitRect.bottom = bitRect.top + ff(theBits.height);
- tempShape = GXNewRectangle(&bitRect);
- }
-
- /* Now take the shape into "root" space. */
-
- GXSetShapeAttributes(tempShape, GXGetShapeAttributes(tempShape) & ~gxMapTransformShape);
-
- GXGetTransformMapping(this->GetTop(), &nestMap);
- GXMapShape(tempShape, &nestMap);
-
- /* Now shape is in "root" space, get its bounds. */
-
- gxTransform aTransform = GXNewTransform();
- GXResetTransform(aTransform);
- GXSetShapeTransform(tempShape, aTransform);
- GXDisposeTransform(aTransform);
-
- /* This one ignores style and transform, but we've taken care of it. */
- GXGetShapeLocalBounds(tempShape, mappedBounds);
- GXDisposeShape(tempShape);
- }
-
- /* ---------------------------------------------------------------------------
- Push
- --------------------------------------------------------------------------- */
-
- void GXBoundingBoxTransformStack::Push(gxTransform aTransform)
- {
- /* Make room for next transform if there isn't any. */
-
- if ( transformCount >= listSize )
- {
- gxTransform *newList = new gxTransform[listSize+10];
- for ( long idx = 0; idx < listSize; ++idx )
- newList[idx] = transformList[idx];
-
- if ( transformList != nil )
- delete [] transformList;
- transformList = newList;
- listSize += 10;
- }
-
- /* Now push the next transform on the stack. */
-
- gxShape clip;
- gxMapping mapping;
- gxTransform newTransform = nil;
-
- if ( transformCount == 0 )
- {
- /* Make the root transform. */
-
- newTransform = GXNewTransform();
- GXResetTransform(newTransform);
- GXGetTransformMapping(aTransform, &mapping);
-
- /* Get the clip and put it into "root" space. */
-
- clip = GXGetTransformClip(aTransform);
- GXSetShapeAttributes(clip, GXGetShapeAttributes(clip) & ~gxMapTransformShape);
- GXMapShape(clip, &mapping);
- GXSetTransformMapping(newTransform, &mapping);
- GXSetTransformClip(newTransform, clip);
-
- /* Now get the bounds of the new root clip. */
-
- GXGetShapeBounds(clip, 0, &rootClipBounds);
-
- GXDisposeShape(clip);
- }
- else
- {
- /* Concatenate this transform with the last one. */
-
- Boolean isIdentity = GXTestTransformIdentity(aTransform, &clip);
-
- if ( isIdentity )
- {
- newTransform = GXCopyToTransform(nil, this->GetTop());
- }
- else
- {
- newTransform = GXNewTransform();
-
- /* Give it the mapping that is the top of the stack mapped by the new mapping. */
-
- gxMapping topMapping;
- GXGetTransformMapping(this->GetTop(), &topMapping);
- GXGetTransformMapping(aTransform, &mapping);
- MapMapping(&mapping, &topMapping);
- GXSetTransformMapping(newTransform, &mapping);
-
- /* Now take the new clip into "root" space. */
-
- clip = GXGetTransformClip(aTransform);
- GXSetShapeAttributes(clip, GXGetShapeAttributes(clip) & ~gxMapTransformShape);
-
- /* Map the clip by the concatenated mapping, puts clip in "root" space. */
-
- GXMapShape(clip, &mapping);
-
- /* Now intersect it with the old "root" clip. */
-
- gxShape rootClip = GXGetTransformClip(this->GetTop());
- GXIntersectShape(rootClip, clip);
- GXSetTransformClip(newTransform, rootClip);
-
- GXGetShapeBounds(rootClip, 0, &(this->rootClipBounds)); /* Update the current clip bounds. */
- GXDisposeShape(rootClip);
- GXDisposeShape(clip);
- }
- }
-
- transformList[transformCount++] = newTransform; /* Push it onto the stack. */
- }
-
- /* ---------------------------------------------------------------------------
- Pop
- --------------------------------------------------------------------------- */
-
- void GXBoundingBoxTransformStack::Pop()
- {
- if ( transformCount > 0 )
- {
- transformCount -= 1;
- GXDisposeTransform(transformList[transformCount]);
- }
-
- gxTransform topTransform = this->GetTop();
- if ( topTransform != nil )
- {
- gxShape topClip = GXGetTransformClip(topTransform);
- GXGetShapeBounds(topClip, 0, &rootClipBounds); /* Reset the root clip bounds box. */
- GXDisposeShape(topClip);
- }
- else
- {
- rootClipBounds.left = gxNegativeInfinity;
- rootClipBounds.top = gxNegativeInfinity;
- rootClipBounds.right = gxPositiveInfinity;
- rootClipBounds.bottom = gxPositiveInfinity;
- }
- }
-
- /* ---------------------------------------------------------------------------
- AccumulateBounds
- --------------------------------------------------------------------------- */
-
- void GXBoundingBoxTransformStack::AccumulateBounds(gxShape aShape)
- {
- gxRectangle thisBounds;
-
- this->GetBoundsOfMappedShape(aShape, &thisBounds);
-
- /* If the thing has a wide open rectange and it isn't a full-shape, then
- this is one of those wierd GX bugs, the shape probably has no bounds.
- */
- if ( (thisBounds.left == gxNegativeInfinity) && (thisBounds.top == gxNegativeInfinity) &&
- (thisBounds.bottom == gxPositiveInfinity) && (thisBounds.right == gxPositiveInfinity) )
- {
- thisBounds.left = gxPositiveInfinity;
- thisBounds.top = gxPositiveInfinity;
- thisBounds.right = gxNegativeInfinity;
- thisBounds.bottom = gxNegativeInfinity;
- }
-
- /* Now intersect it with the bounds of the "root" clip. */
-
- if ( thisBounds.left < rootClipBounds.left )
- thisBounds.left = rootClipBounds.left;
- if ( thisBounds.top < rootClipBounds.top )
- thisBounds.top = rootClipBounds.top;
- if ( thisBounds.bottom > rootClipBounds.bottom )
- thisBounds.bottom = rootClipBounds.bottom;
- if ( thisBounds.right > rootClipBounds.right )
- thisBounds.right = rootClipBounds.right;
-
- /* Now accumulate the bounds into the total. */
-
- if ( thisBounds.left < accumulatedBounds.left )
- accumulatedBounds.left = thisBounds.left;
- if ( thisBounds.right > accumulatedBounds.right )
- accumulatedBounds.right = thisBounds.right;
- if ( thisBounds.top < accumulatedBounds.top )
- accumulatedBounds.top = thisBounds.top;
- if ( thisBounds.bottom > accumulatedBounds.bottom )
- accumulatedBounds.bottom = thisBounds.bottom;
- }
-
- /* ---------------------------------------------------------------------------
- ComputePictureBounds
- --------------------------------------------------------------------------- */
-
- static void ComputePictureBounds(gxShape source, GXBoundingBoxTransformStack *tStack)
- {
- tStack->Push(GXGetShapeTransform(source));
-
- gxShapeType theType = GXGetShapeType(source);
-
- if ( theType == gxPictureType )
- {
- gxStyle overridingStyle, oldStyle;
- gxTransform oldTransform, overridingTransform; /* oldTransform declared above. */
- gxShape child;
-
- long nShapes = GXGetPictureParts(source, 1, gxSelectToEnd, nil, nil, nil, nil);
-
- for ( long idx = 1; idx <= nShapes; ++idx )
- {
- /* Get the next child from the picture. */
-
- GXGetPictureParts(source, idx, 1, &child, &overridingStyle, nil, &overridingTransform);
-
- /* Now apply the overriding attributes (we don't care about Ink, though
- because it does not affect bounds).
- */
- if ( overridingStyle )
- {
- oldStyle = GXCloneStyle(GXGetShapeStyle(child));
- GXSetShapeStyle(child, overridingStyle);
- }
- if ( overridingTransform )
- {
- oldTransform = GXCloneTransform(GXGetShapeTransform(child));
- GXSetShapeTransform(child, overridingTransform);
- }
-
- ComputePictureBounds(child, tStack);
-
- /* Now restore the shapes orginal attributes if we modified them with
- overrides.
- */
- if ( overridingStyle )
- {
- GXSetShapeStyle(child, oldStyle);
- GXDisposeStyle(oldStyle);
- }
- if ( overridingTransform )
- {
- GXSetShapeTransform(child, oldTransform);
- GXDisposeTransform(oldTransform);
- }
- }
- }
- else if ( theType != gxEmptyType )
- {
- tStack->AccumulateBounds(source);
- }
-
- tStack->Pop();
- }
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- /* ---------------------------------------------------------------------------
- GetShapeLocalBounds
- --------------------------------------------------------------------------- */
-
- gxRectangle* GetShapeLocalBounds(gxShape source, gxRectangle *bounds)
- {
- GXBoundingBoxTransformStack tStack;
- gxRectangle accumulatedBounds;
-
- ComputePictureBounds(source, &tStack);
- *bounds = *(tStack.GetAccumulatedBounds(&accumulatedBounds));
-
- return bounds;
- }
-
- #ifdef __cplusplus
- }
- #endif
-